
package com.wilschrader.demarche;

import java.util.EnumSet;

import com.wilschrader.demarche.util.MersenneTwisterFast;

/**
 * Abstract unit superclass that all other units are based upon.
 * 
 * @author Wil Schrader
 * @modifications
 *	08172011	Now uses the new UnitContainer superclass.
 *				Now uses the temporary DamageModel.
 * 	08152011	Changed to abstract class.
 * 				Moved in capacity and special attacks to unify units.
 * 				A unit can now have an unlimited number of attack types.
 * 
 */
public abstract class Unit extends UnitContainer implements Cloneable, Comparable<Unit> {
	
	public static enum Type {
		Ground,
		Fighter,
		Cannon,
		Destroyer,
		Cruiser,
		Carrier,
		Dreadnaught,
		Warsun
	}
	
	public static enum Weapon {
		Main,
		Special,
		Third,
		Fourth,
		Fifth
	}
	
	public static EnumSet<Unit.Type> SPACE_UNITS = EnumSet.of(Unit.Type.Fighter, Unit.Type.Destroyer, Unit.Type.Cruiser, Unit.Type.Carrier, Unit.Type.Dreadnaught, Unit.Type.Warsun);
	public static EnumSet<Unit.Type> GROUND_UNITS = EnumSet.of(Unit.Type.Ground);
	
	protected Type unitType;
	protected int cost;
	protected int[] combatValue;
	protected int[] shotCount;
	protected int capacity;
	protected boolean damaged = false;
	protected boolean sustainsDamage;
	
	/**
	 * @param unitType			The type of the unit.
	 * @param cost				How much the unit cost to construct. Used primarily in unit 'worth' sorting.
	 * @param combatValue		The combat value (CV) of a unit. Reversed standard (ie. 3- instead of 8+).
	 * @param shots				The number of shots the unit fires.
	 * @param capacity			The capacity of a unit, assuming a unit can carry other units.
	 * @param sustainsDamage	If a unit can sustain damage.
	 */
	public Unit(Type unitType, int cost, int[] combatValue, int[] shots, int capacity, boolean sustainsDamage) {
		this.unitType = unitType;
		this.cost = cost;
		this.combatValue = combatValue;
		this.shotCount = shots;
		this.capacity = capacity;
		this.sustainsDamage = sustainsDamage;
	}

	/**
	 * Called when this unit should "fire" its main weapon.
	 * 
	 * @param mt	MersenneTwisterFast instance being used for this simulation
	 * @return		Number of hits that this unit inflicted
	 */
	public int doAttack(MersenneTwisterFast mt) {
		return doAttack(mt, Unit.Weapon.Main);
	}
	
	/**
	 * Called when a unit should "fire" a specific weapon.
	 * 
	 * @param mt	MersenneTwisterFast instance being used for this simulation
	 * @param index The specific index that should be used for CV and Shots
	 * @return Number of hits that this unit inflicted
	 */
	public int doAttack(MersenneTwisterFast mt, Weapon weapon) {
		int hits = 0;

		for (int shot = 0; shot < shotCount[weapon.ordinal()]; shot++) {
			if (mt.nextInt(10) <= (combatValue[weapon.ordinal()] -1)) {
				hits++;
			}
		}
		
		Simulation.debugOutput("Unit (" + this.getClass().getSimpleName() + ") fired " + shotCount[weapon.ordinal()] + " shots, hit " + hits + ".");
		
		return hits;
	}
	
	/**
	 * Called when the unit should take damage.
	 * 
	 * @return True if this unit was destroyed
	 */
	public boolean damage() {
		if (sustainsDamage) {
			if (!damaged) {			
				damaged = true;
				return false;
			}
		}
		return true;
	}
	
	public Type getType() {
		return unitType;
	}
	
	public int getCost() {
		return cost;
	}
	
	public int getCombatValue() {
		return getCombatValue(0);
	}
	
	public int getCombatValue(int index) {
		return combatValue[index];
	}
	
	public int[] getShots() {
		return shotCount;
	}
	
	public int getShotCount() {
		return getShotCount(0);
	}
	
	public int getShotCount(int index) {
		return shotCount[index];
	}
	
	public int getCapacity() {
		return capacity;
	}
	
	public boolean isDamaged() {
		return damaged;
	}
	
	public boolean sustainsDamage() {
		return sustainsDamage;
	}

	@Override
	public abstract Unit clone() throws CloneNotSupportedException;

	@Override
	public boolean equals(Object object) {
		Unit unit = null;
		
		try {
			unit = (Unit) object;
		} catch (ClassCastException e) {
			return false;
		}
		
		if (this.unitType == unit.getType() &&
				this.cost == unit.getCost() &&
				this.shotCount == unit.getShots() &&
				this.sustainsDamage == unit.sustainsDamage)
		{
			return true;
		}
		return false;
	}

	@Override
	public int compareTo(Unit unit) {
		return Simulation.getDamageModel().compare(this, unit);
	}
}
